package modelo;

import ar.uba.fi.algo3.titiritero.ObjetoVivo;
import ar.uba.fi.algo3.titiritero.Posicionable;
import geometria.*;

/**
 * Clase madre de todas las aeronaves, con el comportamiento comun.
 * 
 * 
 */
public abstract class AeroNave implements ObjetoVivo, Posicionable {

	protected Coordenada posicion;
	protected Velocidad velocidad;
	protected Figura limites;
	protected Trayectoria trayectoria;
	protected Coordenada anteUltimoPunto;
	protected Double carreteo, peso, alto, ancho;
	protected Boolean aterrizado = false;
	protected Double moduloVelocidad = 0D;

	public AeroNave(Coordenada pos) {
		this.posicion = pos;
		trayectoria = new Trayectoria();
		this.agregarPuntoATrayectoria(pos);
		this.anteUltimoPunto = pos;

	}

	public Velocidad getVelocidad() {
		return velocidad;
	}

	public boolean esAterrizableEn(Pista unaPista) {
		return unaPista.soportaCarreteo(this.carreteo)
				&& unaPista.soportaPeso(this.peso);
	}

	public boolean chocoContra(AeroNave otra) {

		Figura limitesOtra = otra.getLimites();
		return limitesOtra.intersecta(this.limites);
	}

	public Coordenada getPosicion() {
		Coordenada c = new Coordenada(posicion.getX(), posicion.getY());
		return c;
	}

	/**
	 * Metodos de la interface posicionable
	 */
	public int getX() {
		return ((int) (this.posicion.getX() - this.ancho / 2));
	}

	public int getY() {
		return ((int) (this.posicion.getY() - this.ancho / 2));
	}

	/**
	 * fin de metodos de la interface posicionable
	 */

	public Double getAncho() {
		return this.ancho;
	}

	private Figura getLimites() {
		return this.limites;
	}

	public boolean contienePunto(Coordenada c) {
		Figura f = this.getLimites();
		return f.contiene(c);
	}

	public void agregarPuntoATrayectoria(Coordenada nuevoPunto) {
		trayectoria.agregarPunto(nuevoPunto);
	}

	protected abstract Double getModuloVelocidadDefault();

	private void actualizarLimites() {
		this.limites = new Circunferencia(this.posicion, this.ancho);
	}

	public void actualizarPosicion(Double tiempo) {

		if (tiempo < 0)
			throw new TiempoInvalidoException();

		if (aterrizado)
			return;

		// Si no tenemos puntos en la trayectoria nos movemos en la direccion
		// que tenemos,
		if (this.trayectoria.esVacia()) {
			this.posicion = this.posicion.moverConVelocidad(this.velocidad,
					tiempo);
			this.posicion = Escenario.getInstance().ajustarCoordenada(posicion);
			this.anteUltimoPunto = this.posicion;
			this.actualizarLimites();
			return;
		}

		try {

			if (this.posicion.coincideCon(
					this.trayectoria.obtenerProximoPunto(), 0D)) {
				// Si no creara la coordenada y simplemente la usara por
				// referencia, mas abajo en
				// donde dice this.posicion.mover, me la modifica y se rompe
				// todo.
				Coordenada aux = trayectoria.obtenerProximoPunto();
				this.anteUltimoPunto = new Coordenada(aux.getX(), aux.getY());

				trayectoria.avanzarPunto();

			}

			this.velocidad = new Velocidad(this.posicion,
					this.trayectoria.obtenerProximoPunto(),
					this.getModuloVelocidadDefault());

			this.posicion.mover(trayectoria.obtenerProximoPunto(), tiempo,
					this.velocidad.getModulo());
			this.posicion = Escenario.getInstance().ajustarCoordenada(posicion);
			this.intentarAterrizar();
		}

		catch (TrayectoriaVaciaException e) {
			this.posicion = this.posicion.moverConVelocidad(this.velocidad,
					tiempo);
			this.posicion = Escenario.getInstance().ajustarCoordenada(posicion);
		}
		this.actualizarLimites();

	}

	private void intentarAterrizar() throws TrayectoriaVaciaException {
		Pista candidata = Escenario.getInstance().pistaEn(
				this.trayectoria.obtenerProximoPunto());
		if (candidata != null) {
			// Estamos parados sobre una pista!
			if (this.esAterrizableEn(candidata)
					&& this.entradaCorrecta(candidata)) {
				this.aterrizado = true;
			}
		}
	}

	public void alterarVelocidad(Double porcentaje) {
		this.moduloVelocidad += porcentaje * this.moduloVelocidad;
		this.velocidad.setModulo(moduloVelocidad);
	}

	protected boolean entradaCorrecta(Pista unaPista) {

		return unaPista.esAproximacionValida(anteUltimoPunto, this.posicion);
	}

	public boolean estaAterrizado() {

		return this.aterrizado;
	}

	@Override
	public void vivir() {
		this.actualizarPosicion(1D);
	}

}
